﻿using System;
using System.Linq;
using System.Reflection;
using System.ServiceModel;
using Common;
using System.Collections.Generic;

namespace EditorServiceLibrary
{
    [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
    public class EditorService : IEditorService
    {
        private const int ServiceID = 2;
        private DataContext context = null;

        public EditorService()
        {
            context = new DataContext();
            Logger.type = "editor";
        }

        public Response GetDeclinedArtiles(string editorID)
        {
            Response response = new Response();
            response.ServiceID = ServiceID;
            try
            {
                int decline = (int)ReviewDecision.DeclineArticle;
                var articles = (from va in context.CheckedArticles 
                               from a in context.Articles 
                               where va.EditorID == editorID && va.ArticleID == a.ID && va.decisionNumber == decline
                               select a);
                if (articles == null)
                {
                    response.Error.Details = "Declined articles for editor " + editorID + " not found";
                    response.Error.TargetSite = MethodBase.GetCurrentMethod().Name;
                    response.Error.Type = ErrorTypes.ObjectNotFound;
                }
                else
                {
                    response.Result.AddRange(articles);
                }
            }
            catch (Exception ex)
            {
                Logger.Write(ex, "Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method");
                response.Error = new Error()
                {
                    TargetSite = ex.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = ex.Message
                };
            }
            return response;
        }

        public Response GetAcceptedArticles(string editorID)
        {
            Response response = new Response();
            response.ServiceID = ServiceID;
            try
            {
                int accept = (int)ReviewDecision.MoveArticleToPortfolio;
                var articles = (from va in context.CheckedArticles
                                from a in context.Articles
                                where va.EditorID == editorID && va.ArticleID == a.ID && va.decisionNumber == accept
                                select a);

                if (articles == null)
                {
                    response.Error.Details = "Accepted articles for editor " + editorID + " not found";
                    response.Error.TargetSite = MethodBase.GetCurrentMethod().Name;
                    response.Error.Type = ErrorTypes.ObjectNotFound;
                }
                else
                {
                    response.Result.AddRange(articles);
                }
            }
            catch (Exception ex)
            {
                Logger.Write(ex, "Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method");
                response.Error = new Error()
                {
                    TargetSite = ex.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = ex.Message
                };
            }
            return response;
        }

        public Response GetReviews(string editorID, bool newOnly)
        {
            Response response = new Response();
            response.ServiceID = ServiceID;
            try
            {
                List<Review> reviews = new List<Review>();
                if (newOnly)
                {
                    int created = (int)ReviewStatus.Created;
                    
                    var enteties = (from r in context.Reviews
                                    from va in context.CheckedArticles
                                    where va.EditorID == editorID && r.ArticleID == va.ArticleID && r.stage == created
                                    select r);
                    reviews.AddRange(enteties);
                }
                else
                {
                    var articles = (from va in context.CheckedArticles
                                    where va.EditorID == editorID select va.ArticleID).ToList();
                    var rev = (from r in context.Reviews select r).ToList();

                    foreach (var r in rev)
                    {
                        if (articles.Contains(r.ArticleID))
                        {
                            reviews.Add(r);
                        }
                    }
                }

                if (reviews.Count == 0)
                {
                    response.Error.Details = "No reviews found for editor '" + editorID + "'";
                    response.Error.TargetSite = MethodBase.GetCurrentMethod().Name;
                    response.Error.Type = ErrorTypes.ObjectNotFound;
                }
                else
                {
                    response.Result.AddRange(reviews);
                }
            }
            catch (Exception ex)
            {
                Logger.Write(ex, "Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method");
                response.Error = new Error()
                {
                    TargetSite = ex.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = ex.Message
                };
            }
            return response;
        }

        public Response GetArticlesFromWareHouse(int pageIndex, int pageSize)
        {
            Response response = new Response();
            response.ServiceID = ServiceID;
            try
            {
                int min = (int)MaturityStages.NewArticle;
                int max = (int)MaturityStages.ReviewCreated;

                var articles = (from a in context.Articles where a.status >= min && a.status <= max select a).ToList();

                if (articles.Count == 0)
                {
                    response.Error.Details = "No articles found in warehouse";
                    response.Error.TargetSite = MethodBase.GetCurrentMethod().Name;
                    response.Error.Type = ErrorTypes.ObjectNotFound;
                }
                else
                {
                    if (pageIndex == -1 && pageSize == -1)
                    {
                        response.Result.AddRange(articles);
                    }
                    else
                    {
                        response.TotalResultsCount = articles.Count;
                        response.Result.AddRange(articles.Skip(pageIndex * pageSize).Take(pageSize));
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Write(ex, "Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method");
                response.Error = new Error()
                {
                    TargetSite = ex.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = ex.Message
                };
            }
            return response;
        }

        public Response GetCheckedArticles(string editorID, int pageIndex, int pageSize)
        {
            Response response = new Response();
            response.ServiceID = ServiceID;
            try
            {
                var articles = (from a in context.Articles from ca in context.CheckedArticles where a.ID == ca.ArticleID select a).ToList();

                if (articles.Count == 0)
                {
                    response.Error.Details = "No articles found for editor '" + editorID + "'";
                    response.Error.TargetSite = MethodBase.GetCurrentMethod().Name;
                    response.Error.Type = ErrorTypes.ObjectNotFound;
                }
                else
                {
                    if (pageIndex == -1 && pageSize == -1)
                    {
                        response.Result.AddRange(articles);
                    }
                    else
                    {
                        response.TotalResultsCount = articles.Count;
                        response.Result.AddRange(articles.Skip(pageSize * pageIndex).Take(pageSize));
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Write(ex, "Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method");
                response.Error = new Error()
                {
                    TargetSite = ex.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = ex.Message
                };
            }
            return response;
        }

        public Response SetArticleForEditor(string articleID, string editorID)
        {
            Response response = new Response();
            response.ServiceID = ServiceID;
            try
            {
                var exists = (from v in context.CheckedArticles where v.ArticleID == articleID && !v.IsVerified select v).SingleOrDefault();
                if (exists != null)
                {
                    response.Error = new Error()
                    {
                        Details = "Article is alredy taken by editor '" + exists.EditorID + "'",
                        TargetSite = MethodBase.GetCurrentMethod().Name,
                        Type = ErrorTypes.ObjectExists
                    };
                }
                else
                {
                    var checkedArticle = new CheckedArticle()
                    {
                        ArticleID = articleID,
                        EditorID = editorID,
                        IsVerified = false
                    };
                    var article = (from a in context.Articles where a.ID == articleID select a).SingleOrDefault();

                    if (article != null)
                    {
                        lock (context)
                        {
                            article.Stage = MaturityStages.OnVerification;
                            article.ArticleEditor = editorID;
                            context.CheckedArticles.Add(checkedArticle);
                            context.SaveChanges();
                        }
                        response.Result.Add(article);
                    }
                    else
                    {
                        response.Error = new Error()
                        {
                            Details = "Article with ID '" + articleID + "' not found.",
                            TargetSite = MethodBase.GetCurrentMethod().Name,
                            Type = ErrorTypes.ObjectNotFound
                        };
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Write(ex, "Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method");
                response.Error = new Error()
                {
                    TargetSite = ex.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = ex.Message
                };
            }
            return response;
        }

        private Error ModifyArticleStage(string articleID, MaturityStages stage)
        {
            Error result = new Error();
            var article = (from a in context.Articles where a.ID == articleID select a).SingleOrDefault();

            if (article == null)
            {
                result.Details = "Article with ID '" + articleID + "' not found.";
                result.TargetSite = MethodBase.GetCurrentMethod().Name;
                result.Type = ErrorTypes.ObjectNotFound;
            }
            else
            {
                lock (context)
                {
                    article.Stage = stage;
                    context.SaveChanges();
                }
            }
            return result;
        }

        public Response AcceptArticle(string articleID)
        {
            Response response = new Response();
            response.ServiceID = ServiceID;
            try
            {
                response.Error = ModifyArticleStage(articleID, MaturityStages.SentForReview);
            }
            catch (Exception ex)
            {
                Logger.Write(ex, "Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method");
                response.Error = new Error()
                {
                    TargetSite = ex.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = ex.Message
                };
            }
            return response;
        }

        public Response ReviewerDecline(string articleID, string reviewID, string editorComment)
        {
            Response response = new Response();
            response.ServiceID = ServiceID;
            try
            {
                response.Error = ModifyArticleStage(articleID, MaturityStages.RejectedArticle);
                if (response.Error.Type == ErrorTypes.NoError)
                {
                    response.Error = ConfirmReview(articleID, reviewID, editorComment, ReviewDecision.DeclineArticle);
                }
            }
            catch (Exception ex)
            {
                Logger.Write(ex, "Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method");
                response.Error = new Error()
                {
                    TargetSite = ex.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = ex.Message
                };
            }
            return response;
        }

        public Response DeclineArticle(string articleID, string editorComment)
        {
            Response response = new Response();
            response.ServiceID = ServiceID;
            try
            {
                response.Error = ModifyArticleStage(articleID, MaturityStages.RejectedArticle);
                if (response.Error.Type == ErrorTypes.NoError)
                {
                    var checkedArticle = (from ca in context.CheckedArticles where ca.ArticleID == articleID && !ca.IsVerified select ca).SingleOrDefault();
                    if (checkedArticle != null)
                    {
                        var review = new Review()
                        {
                            EditorComment = editorComment,
                            AuthorID = checkedArticle.EditorID,
                            Decision = ReviewDecision.DeclineArticle,
                            Status = ReviewStatus.Approved,
                            AttachmentID = "no attachment"
                        };
                        lock (context)
                        {
                            context.Reviews.Add(review);
                            checkedArticle.IsVerified = true;
                            checkedArticle.Decision = ReviewDecision.DeclineArticle;
                            context.SaveChanges();
                        }
                    }
                    else
                    {
                        response.Error = new Error()
                        {
                            Details = "Article with id '" + articleID + "' not found in articles, checked by current editor.",
                            TargetSite = MethodBase.GetCurrentMethod().Name,
                            Type = ErrorTypes.ObjectNotFound
                        };
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Write(ex, "Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method");
                response.Error = new Error()
                {
                    TargetSite = ex.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = ex.Message
                };
            }
            return response;
        }

        private Error ConfirmReview(string articleID, string reviewID, string editorComment, ReviewDecision decision)
        {
            Error result = new Error();
            var checkedArticle = (from ca in context.CheckedArticles where ca.ArticleID == articleID && !ca.IsVerified select ca).SingleOrDefault();

            if (checkedArticle == null)
            {
                result.Details = "Article with id '" + articleID + "' not found in articles, checked by current editor.";
                result.TargetSite = MethodBase.GetCurrentMethod().Name;
                result.Type = ErrorTypes.ObjectNotFound;
            }
            else
            {
                var review = (from r in context.Reviews where r.ID == reviewID select r).SingleOrDefault();
                if (review == null)
                {
                    result.Details = "Review with ID '" + reviewID + "' not found.";
                    result.TargetSite = MethodBase.GetCurrentMethod().Name;
                    result.Type = ErrorTypes.ObjectNotFound;
                }
                else
                {
                    lock (context)
                    {
                        review.Status = ReviewStatus.Approved;
                        review.Decision = decision;
                        review.EditorComment = editorComment;
                        checkedArticle.IsVerified = true;
                        checkedArticle.Decision = decision;
                        context.SaveChanges();
                    }
                }
            }
            return result;
        }

        public Response SendForRevision(string articleID, string reviewID, string editorComment)
        {
            Response response = new Response();
            response.ServiceID = ServiceID;
            try
            {
                response.Error = ModifyArticleStage(articleID, MaturityStages.SentForRevision);
                if (response.Error.Type == ErrorTypes.NoError)
                {
                    response.Error = ConfirmReview(articleID, reviewID, editorComment, ReviewDecision.SendArticleForRevision);
                }
            }
            catch (Exception ex)
            {
                Logger.Write(ex, "Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method");
                response.Error = new Error()
                {
                    TargetSite = ex.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = ex.Message
                };
            }
            return response;
        }

        public Response AcceptReview(string reviewID)
        {
            Response response = new Response();
            response.ServiceID = ServiceID;
            try
            {
                var review = (from r in context.Reviews where r.ID == reviewID select r).SingleOrDefault();

                if (review != null)
                {
                    lock (context)
                    {
                        review.Status = ReviewStatus.Approved;
                        context.SaveChanges();
                    }
                }
                else
                {
                    response.Error = new Error()
                    {
                        Details = "Review with id '" + reviewID + "' not found",
                        TargetSite = MethodBase.GetCurrentMethod().Name,
                        Type = ErrorTypes.ObjectNotFound
                    };
                }
            }
            catch (Exception ex)
            {
                Logger.Write(ex, "Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method");
                response.Error = new Error()
                {
                    TargetSite = ex.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = ex.Message
                };
            }
            return response;
        }

        public Response DeclineReview(string reviewID)
        {
            Response response = new Response();
            response.ServiceID = ServiceID;
            try
            {
                var review = (from r in context.Reviews where r.ID == reviewID select r).SingleOrDefault();

                if (review != null)
                {
                    lock (context)
                    {
                        review.Status = ReviewStatus.Declined;
                        context.SaveChanges();
                    }
                }
                else
                {
                    response.Error = new Error()
                    {
                        Details = "Review with id '" + reviewID + "' not found",
                        TargetSite = MethodBase.GetCurrentMethod().Name,
                        Type = ErrorTypes.ObjectNotFound
                    };
                }
            }
            catch (Exception ex)
            {
                Logger.Write(ex, "Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method");
                response.Error = new Error()
                {
                    TargetSite = ex.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = ex.Message
                };
            }
            return response;
        }

        public Response MoveToArticlePortfolio(string articleID, string reviewID, string editorComment)
        {
            Response response = new Response();
            response.ServiceID = ServiceID;
            try
            {
                response.Error = ModifyArticleStage(articleID, MaturityStages.AddedToPortfolio);
                if (response.Error.Type == ErrorTypes.NoError)
                {
                    if (response.Error.Type == ErrorTypes.NoError)
                    {
                        response.Error = ConfirmReview(articleID, reviewID, editorComment, ReviewDecision.MoveArticleToPortfolio);
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Write(ex, "Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method");
                response.Error = new Error()
                {
                    TargetSite = ex.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = ex.Message
                };
            }
            return response;
        }

        public Response GetArticlesWithReviews(string editorID)
        {
            Response response = new Response();
            response.ServiceID = ServiceID;
            try
            {
                var takenArticles = (from ca in context.CheckedArticles
                                     where ca.EditorID == editorID
                                     select ca.ArticleID).Intersect(from r in context.Reviews select r.ArticleID);

                var articles = (from a in context.Articles from ta in takenArticles where a.ID == ta select a).ToList();
                if (articles.Count == 0)
                {
                    response.Error.Details = "No reviews found for editor '" + editorID + "'";
                    response.Error.TargetSite = MethodBase.GetCurrentMethod().Name;
                    response.Error.Type = ErrorTypes.ObjectNotFound;
                }
                else
                {
                    response.Result.AddRange(articles);
                }
            }
            catch (Exception ex)
            {
                Logger.Write(ex, "Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method");
                response.Error = new Error()
                {
                    TargetSite = ex.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = ex.Message
                };
            }
            return response;
        }
    }
}